package avicit.bdp.oss.utils.upload.service;

import avicit.bdp.oss.utils.upload.entity.UploadDto;
import avicit.bdp.oss.utils.upload.utils.common.CommUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @金航数码科技有限责任公司
 * @作者：developer
 * @邮箱：developer@avic-digital.com
 * @创建时间： 2020-12-09
 * @类说明：AbstractUploadService
 * @修改记录：
 * @注意事项：
 * @主要功能：断点/分片上传骨骼类
 */
public abstract class AbstractUploadService implements UploadService {
    private static final Logger logger = LoggerFactory.getLogger(AbstractUploadService.class);

    /**
     * 入参合法性检查
     * @param uploadDto
     */
    public abstract void validParameter(UploadDto uploadDto);

    /**
     * 保存上传Task
     * @param uploadDto
     * @return
     */
    public abstract <T> T initiateUploadTask(UploadDto uploadDto);

    /**
     * 检查分片是否已存在
     * @param uploadDto
     * @return
     */
    public abstract boolean checkChunkExist(UploadDto uploadDto);

    /**
     * 保存单个分片
     * @param uploadDto
     * @return
     */
    public abstract String uploadSingleChunk(UploadDto uploadDto);

    /**
     * 保存分片元数据
     * @param uploadDto
     */
    public abstract void saveChunkMeta(UploadDto uploadDto);

    /**
     * 检查所有分片是否上传完成
     * @param uploadDto
     * @return
     */
    public abstract boolean checkChunkCompleted(UploadDto uploadDto);

    /**
     * 合并所有chunk
     * @param uploadDto
     */
    public abstract void mergeAllChunks(UploadDto uploadDto);

    /**
     * 清理碎片，包括redis中分片元数据、Minio中分片文件、合并失败的文件
     * @param uploadDto
     */
    public abstract void cleanFragment(UploadDto uploadDto);

    /**
     * 处理异常
     * @param uploadDto
     */
    public abstract void processUploadFail(UploadDto uploadDto);

    /**
     * 保存对象信息
     * @param uploadDto
     */
    public abstract void saveObjectInfo(UploadDto uploadDto);

    /**
     * 判断文件是否已经完整上传
     * @param uploadDto
     * @return
     */
    public abstract boolean checkFileUploadCompleted(UploadDto uploadDto);

    /**
     * 分布式锁
     * @param uploadDto
     */
    public abstract void dlmLock(UploadDto uploadDto);
    public abstract void dlmUnlock(UploadDto uploadDto);

    /**
     * 上传分片
     * @param uploadDto
     */
    public void uploadChunks(UploadDto uploadDto) {
        // step1:参数合法性检查
        validParameter(uploadDto);

        try {
            // step2:检查chunk是否已存在，如果是，直接返回；否则，继续上传
            if (checkChunkExist(uploadDto)) {
                logger.warn("文件{}分片{}已存在", uploadDto.getFile().getName(), uploadDto.getChunkNo());
                return;
            }

            // step3:上传分片数据
            uploadSingleChunk(uploadDto);

            // step4:保存单个chunk
            saveChunkMeta(uploadDto);
        } catch (Exception e) {
            processUploadFail(uploadDto);
            CommUtil.printLogAndThrowException(String.format("上传分片失败，errMsg=%s", e));
        }
    }

    /**
     * 合并分片
     * @param uploadDto
     */
    public void mergeChunks(UploadDto uploadDto) {
        // step1:参数合法性检查
        validParameter(uploadDto);

        try {
            // step2:判断所有chunk是否已完成
            if (!checkChunkCompleted(uploadDto)) {
                CommUtil.printLogAndThrowException(String.format("任务id[%s]]没有上传完所有分片", uploadDto.getFileMd5()));
            }

            // step3:合并文件
            mergeAllChunks(uploadDto);

            // step4:保存文件元数据
            saveObjectInfo(uploadDto);

            // step5:清理碎片
            cleanFragment(uploadDto);
        } catch (Exception e) {
            processUploadFail(uploadDto);
            CommUtil.printLogAndThrowException(String.format("合并分片失败，errMsg=%s", e));
        }
    }
}
